#include "paragraph-convert.h"

#include "util/logger/logger.h"
#include "util/html/escape.h"


namespace book
{

static const char *title() {
    return "ParagraphConvert ";
}

ParagraphConvert::ParagraphConvert(CharInputStream &in, std::ostream &out)
    : tokenizer_(in)
    , out_(out) {
}

void ParagraphConvert::operator()() {
    while (MdToken tok = tokenizer_.next()) {
        handle(tok);
    }
    handleRemainFlag();
}

void ParagraphConvert::handle(const MdToken &tok) {
    if (tok.type() == MdToken::Type::Text) {
        handleText(tok.text());
    } else if (tok.type() == MdToken::Type::Code) {
        handleCode(tok.text());
    } else {
        handleFlag(tok.type());
    }
}

void ParagraphConvert::handleText(const std::string &text) {
    out_ << html::Escape(text);
}

static void printHtmlTag(std::ostream &out, MdToken::Type type, bool isRight) {
    const char *bias = isRight ? "/" : "";
    using Type = MdToken::Type;
    switch (type) {
    case Type::StarItalic:
    case Type::UnderlineItalic:
        out << "<" << bias << "i>";
        break;
    case Type::StarBold:
    case Type::UnderlineBold:
        out << "<" << bias << "b>";
        break;
    case Type::StarBoldItalic:
    case Type::UnderlineBoldItalic:
        if (isRight) {
            out << "</i></b>";
        } else {
            out << "<i><b>";
        }
        break;
    case Type::DeleteLine:
        out << "<" << bias << "del>";
        break;
    default:
        break;
    }
}

// 判断这种标记是否是简单的装饰标记，即只需要在文本内容的两边加上没有属性的html标签即可
// 这类标签可以用同一套逻辑简单处理
static bool isSimpleDecorationFlag(MdToken::Type type) {
    using Type = MdToken::Type;
    switch (type) {
    case Type::StarItalic:
    case Type::StarBold:
    case Type::StarBoldItalic:
    case Type::UnderlineItalic:
    case Type::UnderlineBold:
    case Type::UnderlineBoldItalic:
    case Type::DeleteLine:
        return true;
    default:
        return false;
    }
}

void ParagraphConvert::handleFlag(MdToken::Type type) {
    if (isSimpleDecorationFlag(type)) {
        handleSimpleDecorationFlag(type);
        return;
    }

    if (type == MdToken::Type::UnnamedHrefLeftSide) {
        handleUnnamedHref();
        return;
    }

    LOGE << "ParagraphConvert::handleFlag unknown type: [" << type << "]";
}

void ParagraphConvert::handleSimpleDecorationFlag(MdToken::Type type) {
    if (flagStack_.empty() || type != flagStack_.top()) {
        printHtmlTag(out_, type, false);
        flagStack_.push(type);
    } else {
        printHtmlTag(out_, type, true);
        flagStack_.pop();
    }
}

void ParagraphConvert::handleCode(const std::string &content) {
    out_ << "<code>";
    out_ << html::Escape(content);
    out_ << "</code>";
}

// 进入这个函数前左侧的 '<' 已经被读取出来了，
// 接下来需要读取超链接内容和右侧的 '>'
void ParagraphConvert::handleUnnamedHref() {
    MdToken textToken = tokenizer_.next();
    if (textToken.type() != MdToken::Type::Text) {
        LOGE << "ParagraphConvert::handleUnnamedHref should be text token, but meet ["
            << textToken.type() << "]";

        // 应该遇到文本符号，若非文本符号则放回
        tokenizer_.pushFront(std::move(textToken));

        // 注意这里应该 return，因为这种情况下超链接并没有有效内容
        return;
    }

    MdToken unnamedHrefRightSideToken = tokenizer_.next();
    if (unnamedHrefRightSideToken.type() != MdToken::Type::UnnamedHrefRightSide) {
        LOGE << "ParagraphConvert::handleUnnamedHref should be 'UnnamedHrefRightSide', but meet ["
            << unnamedHrefRightSideToken.type() << "]";

        // 应该遇到右侧的 '>'，如果不是这个符号，应该让链接正常输出，同时把这个符号放回
        tokenizer_.pushFront(std::move(unnamedHrefRightSideToken));
        // 注意这里不要 return，这种情况只是 markdown 的书写不标准，报错即可，不影响超链接的正常展示
    }

    out_ << "<a href='" << textToken.text() << "' target='_blank'>";
    out_ << textToken.text();
    out_ << "</a>";
}

void ParagraphConvert::handleRemainFlag() {
    while (!flagStack_.empty()) {
        printHtmlTag(out_, flagStack_.pop(), true);
    }
}

} // namespace book
